
#include <assert.h>
#include <string.h>
#include <stdarg.h>

#include <sys/types.h>
#include <unistd.h>

#include "log.h"




static __inline const char *shortname(const char *filename)
{
    return strrchr(filename, '/') != 0 ? strrchr(filename, '/') + 1 : filename;
}

static void log_safe_free(log_t *log)
{
    if (log == NULL)
        return;

    pthread_mutex_destroy(&log->log_metex);
   
    free(log);
    log = NULL;
}

static char *getCurrentTime()
{
    static char time_buf[1024];
    memset(time_buf, 0, 1024);
    time_t ts;
    time(&ts);
    ctime_r(&ts, time_buf);

    time_buf[strlen(time_buf) - 1] = '\0';

    return time_buf;
}

static void log_print_terminal(const char *str)
{
    printf("%s\n", str);
}

static void log_save_flash(const char *filename, const char *str)
{
    FILE *pfile = NULL;
    pfile = fopen(filename, "a+");
    if (pfile == NULL)
    {
        pfile = fopen(filename, "w+");
		if (pfile == NULL) return;
    }

    fwrite("\n", strlen("\n"), 1, pfile);
    fwrite(str, strlen(str), 1, pfile);
    fclose(pfile);
}

log_t *log_init(const char *logfile, LOG_SAVE_FLAG saveFlag, LOG_LEVEL level)
{
    assert(logfile != NULL);

    log_t *log = (log_t *)malloc(sizeof(log_t));
    if (log != NULL)
    {
        memset(log, 0, sizeof(log_t));
        strncpy(log->filename, logfile, strlen(logfile));
        log->saved = saveFlag;
        log->level = level;

        pthread_mutex_init(&log->log_metex, NULL);
    }

    return log;
}

static const char *log_level_str[] = {
    [LOG_LEVEL_EMERG] = "EMERG",
    [LOG_LEVEL_ALERT] = "ALERT",
    [LOG_LEVEL_CRITICAL] = "CRITICAL",
    [LOG_LEVEL_ERROR] = "ERROR",
    [LOG_LEVEL_WARNING] = "WARNING",
    [LOG_LEVEL_NOTICE] = "NOTICE",
    [LOG_LEVEL_INFO] = "INFO",
    [LOG_LEVEL_DEFAULT] = "DEFAULT"};

__inline void log_print(const char *filename, int line, LOG_LEVEL level, log_t *log_type, const char *tag, char *format, ...)
{
    if (level > log_type->level)
    {
        return;
    }

    pthread_mutex_lock(&log_type->log_metex);
    memset(log_type->logBuff, 0, LOG_SIZE);

    switch (level)
    {
    case LOG_LEVEL_CUSTOM:
        snprintf(log_type->logBuff, LOG_SIZE, "[%s] [%s] [%u] (%s:%d)",
                 tag, getCurrentTime(), (unsigned int)getpid(), filename, line);
        break;

    case LOG_LEVEL_EMERG:
    case LOG_LEVEL_ALERT:
    case LOG_LEVEL_CRITICAL:
    case LOG_LEVEL_ERROR:
    case LOG_LEVEL_WARNING:
    case LOG_LEVEL_NOTICE:
    case LOG_LEVEL_INFO:
    case LOG_LEVEL_DEFAULT:
        snprintf(log_type->logBuff, LOG_SIZE, "[%s] [%s] [%u] (%s:%d)",
                 log_level_str[level], getCurrentTime(),  (unsigned int)getpid(), filename, line);
        break;

    default:

        break;
    }

    va_list args;
    int offset = strlen(log_type->logBuff);

    va_start(args, format);
    vsnprintf(log_type->logBuff + offset, LOG_SIZE - offset - 1, format, args);
    va_end(args);

    switch (log_type->saved)
    {
    case LSF_PRINT:
        log_print_terminal(log_type->logBuff);
        break;

    case LSF_FLASH:
        log_save_flash(log_type->filename, log_type->logBuff);
        break;

    case LSF_FULL:
        log_print_terminal(log_type->logBuff);
        log_save_flash(log_type->filename, log_type->logBuff);
        break;

    default:

        break;
    }

    pthread_mutex_unlock(&log_type->log_metex);
}

void log_destroy(log_t *log_type)
{
    log_safe_free(log_type);
}